
#pragma once
#include "StdAfx.h"
#include "ASTBuilder.h"
#include "ASTNode.h"

namespace AST{

struct ASTBuildContext{
//	AST::Node* parentNode;
	AST::ASTree* tree;
//	std::stack<AST::Node*,std::vector<AST::Node*>> pendingTraversal;
//	std::stack<CXCursor> pendingTraversal;
	std::ofstream log;
//	CXCursor parentCursor;
	std::map<unsigned int,AST::Node*> nodeMap;
	AST::Builder* builder;
};

Builder::Builder(void){}

Builder::~Builder(void){
  clang_disposeTranslationUnit(TU);
  clang_disposeIndex(index);
}

std::string& Builder::source(){
	return sourceString;
}


void Builder::buildAST(char* filename, ASTree& tree){
	index = clang_createIndex(0, 0);
	char* cliArgs = "";
	
	if (!srcMgr.addSource(filename))
		exit(7);

	//Parse file
	TU = clang_parseTranslationUnit(index, filename, 0, 0, 0, 0,   CXTranslationUnit_None);
	
	//Prepare to visit clang's AST
	CXCursor cursor = clang_getTranslationUnitCursor(TU);
	CXChildVisitResult (* myVisitor) (CXCursor cursor, CXCursor parent, CXClientData client_data) = visitorFuction;
	
	//Create and init traversal context
	ASTBuildContext context;
	context.nodeMap.clear();
	context.nodeMap[clang_hashCursor(cursor)] = tree.addNode(clang_getCString(clang_getCursorKindSpelling(clang_getCursorKind(cursor))),cursor);
	context.tree = &tree;
	context.builder = this;
	std::string debugLog = tree.getName();
	debugLog.append("BuilderLog.txt");
	context.log.open(debugLog.c_str());

	//visit children (breadth-wise)
	clang_visitChildren(cursor, myVisitor, &context);
	context.log.close();
}


CXChildVisitResult visitorFuction(CXCursor cursor, CXCursor parent, CXClientData client_data){
	//useful shorthands
	ASTBuildContext* context = (ASTBuildContext*)client_data;
	AST::ASTree *tree = context->tree;
	std::ofstream& log = context->log;
	AST::Node* parentNode = context->nodeMap[clang_hashCursor(parent)];

	//parent cannot be a leaf now, setting it so
	parentNode->leaf(false);

    CXFile sourceFile;
    unsigned int line, column, offset;
	CXSourceRange range= clang_getCursorExtent(cursor);
	CXSourceLocation loc =  clang_getCursorLocation(cursor);
	clang_getSpellingLocation(loc, &sourceFile, &line, &column, &offset);
	std::string filename;
	std::string * source = NULL;
	if (sourceFile!=NULL){
		filename = std::string(clang_getCString(clang_getFileName(sourceFile)));
		source = context->builder->srcMgr.getSource(filename);
		if (source!=NULL)
			log << "got source!" <<std::endl;
	}
	else
		filename = std::string("NULL");

	log << filename << std::endl;

	//generate child node name
	CXString cursorDisplayName = clang_getCursorDisplayName(cursor);
	std::string nodeName = std::string(clang_getCString(clang_getCursorKindSpelling(clang_getCursorKind(cursor))));
	std::stringstream str, refeeName;
	std::string value = std::string(clang_getCString(cursorDisplayName));
	int size; 
	std::string sourcePiece;

	if (source!=NULL){
		size=range.end_int_data - range.begin_int_data;
		if (size>0)
			sourcePiece = source->substr(range.begin_int_data-2,range.end_int_data - range.begin_int_data);
	}
	str << nodeName << ":" << std::string(clang_getCString(clang_getTypeKindSpelling ((clang_getCursorType(cursor).kind)))) << "(" << value << ")";
	log << "-----------------------------------------------------------------------------" << std::endl;
	CXChildVisitResult nextAction;

	if (tree->recurseCursor(&cursor) == AST::ASTree::RECURSE){
		fprintf(stderr, "Accepted the following cursor:\n");
		log << std::string("Accepted: ") << tree->getNodeCount() << ":" << str.str() << std::endl;
		log << "Source:" << std::endl << sourcePiece << std::endl;
		Node* newNode = tree->addChild(parentNode, str.str(), cursor); //add child to ASTree
		context->nodeMap[clang_hashCursor(cursor)] = newNode;
		if (source!=NULL)
			newNode->nodeSource(sourcePiece);
		nextAction = CXChildVisit_Recurse;
	}
	else{ 
		fprintf(stderr, "Ignored the following cursor:\n");
		log << std::string("Ignoring: ") << str.str() << std::endl;
		nextAction = CXChildVisit_Continue;
	}
	

	if (clang_isDeclaration(clang_getCursorKind(cursor))){
		CXCursor refee = clang_getCursorDefinition (cursor);
		loc =  clang_getCursorLocation(refee);
		clang_getSpellingLocation(loc, &sourceFile, &line, &column, &offset);
		std::string refeeName = std::string(clang_getCString(clang_getCursorKindSpelling(clang_getCursorKind(refee))));
		std::string refeeFile =  (sourceFile!=NULL)?clang_getCString(clang_getFileName(sourceFile)):std::string("NULL");
		log << "definition: " << refeeName << " in: " <<  refeeFile << std::endl;
		log << "definition? Cursor: " << clang_isCursorDefinition (cursor) << " - Refee: " << clang_isCursorDefinition(refee) << std::endl;
		
	}

	CXCursorKind  kind = clang_getCursorKind(cursor);
	log << "|dec?" << clang_isDeclaration (kind) << "|ref?" << clang_isReference (kind) << "|exp?" << clang_isExpression (kind)
		<< "|stm?" << clang_isStatement (kind)	<< "|inv?" << clang_isInvalid (kind)	<< "|trn?" << clang_isTranslationUnit (kind)
		<< "|pre?" << clang_isPreprocessing (kind)	<< "|nxp?" << clang_isUnexposed (kind) << std::endl;

	log << "lexical parent " << clang_getCString(clang_getCursorDisplayName(clang_getCursorLexicalParent(cursor))) << std::endl;

	fprintf(stderr, "visitor: data=%s, cursorDispName=%s\n", str.str().c_str(), cursorDisplayName);
	fprintf(stderr, "file: %s\n", clang_getCString(clang_getFileName(sourceFile)));
	return nextAction;
}


}// namespace AST